home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Source / WAIS / next-ui / IconWell.m < prev    next >
Encoding:
Text File  |  1992-07-27  |  11.5 KB  |  493 lines

  1. // IconWell.m
  2. //
  3. // Free software created 1 Feb 1992
  4. // by Paul Burchard <burchard@math.utah.edu>.
  5.  
  6. #import "IconWell.h"
  7. #import "IconWellControl.h"
  8. #import <appkit/appkit.h>
  9. #import <objc/Storage.h>
  10.  
  11.  
  12. @implementation IconWell
  13.  
  14. static id windowList;
  15. static id wellsByWindow;
  16.  
  17. + initialize
  18. {
  19.     if(self == [IconWell class])
  20.     {
  21.     windowList = [[List alloc] initCount:0];
  22.     wellsByWindow = [[List alloc] initCount:0];
  23.     }
  24.     return self;
  25. }
  26.  
  27. + wellListFor:aWindow
  28. {
  29.     int index;
  30.     
  31.     index = [windowList indexOf:aWindow];
  32.     if(index == NX_NOT_IN_LIST) return nil;
  33.     return [wellsByWindow objectAt:index];
  34. }
  35.  
  36. - initFrame:(const NXRect *)frameRect
  37. {
  38.     id theCell;
  39.     
  40.     [super initFrame:frameRect];
  41.     
  42.     theCell = [[ActionCell alloc] initIconCell:"Blank.tiff"];
  43.     [theCell setBezeled:YES];
  44.     [self setCell:theCell];
  45.     
  46.     iconPath = [[Storage alloc] initCount:0 elementSize:sizeof(char) description:"c"];
  47.     sprintf(iconName, "IconWell-%ld", (long)[self self]);
  48.  
  49.     isHoldOnDrag = YES;
  50.     [self setDraggable:YES droppable:YES];
  51.     return self;
  52. }
  53.  
  54. - free
  55. {
  56.     int index;
  57.     id list;
  58.     
  59.     [iconPath free];
  60.     if(list = [IconWell wellListFor:window])
  61.     {
  62.         [list removeObject:self];
  63.     if([list count] <= 0)
  64.     {
  65.         index = [wellsByWindow indexOf:list];
  66.         [wellsByWindow removeObjectAt:index];
  67.         [windowList removeObjectAt:index];
  68.         [list free];
  69.     }
  70.     }
  71.     return [super free];
  72. }
  73.  
  74. - windowChanged:newWindow
  75. {
  76.     id list;
  77.     
  78.     // Enter new well into global list.
  79.     if(window || !newWindow) return nil;//!!!
  80.     if(!(list = [IconWell wellListFor:newWindow]))
  81.     {
  82.         list = [[List alloc] initCount:0];
  83.     [windowList addObject:newWindow];
  84.     [wellsByWindow addObject:list];
  85.     }
  86.     [list addObjectIfAbsent:self];
  87.     return self;
  88. }
  89.  
  90. - setBezeled:(BOOL)flag
  91. {
  92.     return [cell setBezeled:flag];
  93. }
  94.  
  95. - (BOOL)isBezeled
  96. {
  97.     return [cell isBezeled];
  98. }
  99.  
  100. - setBordered:(BOOL)flag
  101. {
  102.     return [cell setBordered:flag];
  103. }
  104.  
  105. - (BOOL)isBordered
  106. {
  107.     return [cell isBordered];
  108. }
  109.  
  110. - setDraggable:(BOOL)dragFlag droppable:(BOOL)dropFlag
  111. {
  112.     isDraggable = dragFlag;
  113.     isDroppable = dropFlag;
  114.     return self;
  115. }
  116.  
  117. - (BOOL)isDraggable
  118. {
  119.     return isDraggable;
  120. }
  121.  
  122. - (BOOL)isDroppable
  123. {
  124.     return isDroppable;
  125. }
  126.  
  127. - setHoldOnDrag:(BOOL)flag
  128. {
  129.     if(isDragging) return nil;
  130.     isHoldOnDrag = flag;
  131.     return self;
  132. }
  133.  
  134. - (BOOL)isHoldOnDrag
  135. {
  136.     return isHoldOnDrag;
  137. }
  138.  
  139.  
  140. - clear:sender
  141. {
  142.     // This does not de-alloc Blank.tiff since it was not renamed.
  143.     // This only affects this IconWell because of the unique iconName.
  144.     [cell setIcon:"Blank.tiff"];
  145.     [[NXImage findImageNamed:iconName] free];
  146.     [iconPath setNumSlots:0];
  147.     return self;
  148. }
  149.  
  150. - (const char *)stringValue
  151. {
  152.     if([iconPath count] <= 0) return 0;
  153.     return (const char *)[iconPath elementAt:0];
  154. }
  155.  
  156. - getTiffForPath:(const char *)pathString
  157. {
  158.     int ok, length;
  159.     char *tiff, *fakePath, *q;
  160.     const char *p;
  161.     NXStream *imageStream;
  162.     id iconImage;
  163.     
  164.     // Ask WorkSpace for correct TIFF corresponding to path list pathString.
  165.     // Since ``full paths'' are required, prepend '/' to each name if missing.
  166.     if(!pathString) return nil;
  167.     if(!(fakePath = (char *)malloc((strlen(pathString)+1)*sizeof(char))))
  168.         return nil;
  169.     for(p=pathString, q=fakePath; *p; p++)
  170.     {
  171.         if(p==pathString && *p!='/') *q++ = '/';
  172.     *q++ = *p;
  173.         if(*p=='\t' && *(p+1)!='/') *q++ = '/';
  174.     }
  175.     *q = 0;
  176.     [[NXApp appSpeaker] setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
  177.     [[NXApp appSpeaker] getFileIconFor:fakePath
  178.         TIFF:&tiff TIFFLength:&length ok:&ok];
  179.     free(fakePath);
  180.  
  181.     // If no icon, use generic ".txt" icon.
  182.     if(!ok)
  183.     {
  184.     [[NXApp appSpeaker]
  185.         setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
  186.     [[NXApp appSpeaker] getFileIconFor:"/file.txt"
  187.         TIFF:&tiff TIFFLength:&length ok:&ok];
  188.     if(!ok) return nil;
  189.     }
  190.     
  191.     // Create NXImage from TIFF data.
  192.     imageStream = NXOpenMemory(tiff, length, NX_READONLY);
  193.     if(!imageStream) return nil;
  194.     iconImage = [[NXImage alloc] initFromStream:imageStream];
  195.     NXClose(imageStream);
  196.     return iconImage;
  197. }
  198.  
  199. - setStringValue:(const char *)aString
  200. {
  201.     id iconImage;
  202.     
  203.     // If path is NULL, clear well.
  204.     if(!aString) [self clear:self];
  205.     
  206.     // Ask WorkSpace for correct TIFF corresponding to path aString.
  207.     if(!(iconImage = [self getTiffForPath:aString])) return nil;
  208.  
  209.     // Enter NXImage into cell, freeing previous image.
  210.     // (Note: Common "Blank.tiff" image is not freed as it was never renamed.)
  211.     [cell setIcon:"Blank.tiff"];
  212.     [[NXImage findImageNamed:iconName] free];
  213.     [iconImage setName:iconName];
  214.     [cell setIcon:iconName];
  215.     
  216.     // Enter new path into iconPath, notify target.
  217.     [iconPath setNumSlots:(strlen(aString)+1)];
  218.     strcpy((char *)[iconPath elementAt:0], aString);
  219.     [self sendAction:[cell action] to:[cell target]];
  220.     return self;
  221. }
  222.  
  223. - takeStringValueFrom:sender
  224. {
  225.     id oldTarget = nil;
  226.     id rtn;
  227.     
  228.     // If sender is target, don't send action (to avoid circularity).
  229.     if(sender == [self target])
  230.         { oldTarget = [self target]; [self setTarget:nil]; }
  231.     rtn = [self setStringValue:[sender stringValue]];
  232.     if(oldTarget) [self setTarget:oldTarget];
  233.     return rtn;
  234. }
  235.  
  236.  
  237. - (BOOL)acceptsFirstMouse
  238. {
  239.     return YES;
  240. }
  241.  
  242. - (int)openFile:(const char *)fullPath ok:(int *)flag
  243. {
  244.     int rtn;
  245.     
  246.     // Ask WorkSpace to open the file.  No fudging paths now.
  247.     [[NXApp appSpeaker] setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
  248.     rtn = [[NXApp appSpeaker] openFile:fullPath ok:flag];
  249.     if(rtn != 0) return(rtn);
  250.     return 0;
  251. }
  252.  
  253. - (int)prepFile:(const char *)fullPath ok:(int *)flag
  254. {
  255.     // Default is no prep needed on drag-out.
  256.     return 0;
  257. }
  258.  
  259. - setDelegate:anObject
  260. {
  261.     delegate = anObject;
  262.     return self;
  263. }
  264.  
  265. - delegate
  266. {
  267.     return delegate;
  268. }
  269.  
  270. - mouseDown:(NXEvent *)theEvent
  271. {
  272.     int ok;
  273.     char *fileName, *nxt;
  274.     NXPoint mousePoint;
  275.     NXRect iconRect;
  276.     id success, handler;
  277.  
  278.     if(theEvent->data.mouse.click < 2)
  279.     {
  280.         // Single click means drag icon out.
  281.  
  282.     // Check if mouse actually clicked on icon.
  283.     mousePoint = theEvent->location;
  284.     [super convertPoint:&mousePoint fromView:nil];
  285.     [self getBounds:&iconRect];
  286.     [cell getIconRect:&iconRect];
  287.     NX_WIDTH(&iconRect) = NX_HEIGHT(&iconRect) = 48.0;
  288.     if(!NXMouseInRect(&mousePoint, &iconRect, [self isFlipped]))
  289.         return self;
  290.     
  291.     // Don't actually start the drag until mouse gets dragged a bit.
  292.     if(isDraggable)
  293.     {
  294.         isDragging = YES;
  295.         [window addToEventMask:NX_LMOUSEDRAGGEDMASK];
  296.         dragFromEvent = *theEvent;
  297.     }
  298.     return self;
  299.     }
  300.     else if(theEvent->data.mouse.click == 2)
  301.     {
  302.         // Double-click means open file(s).
  303.     
  304.     // Delegate this task if possible.
  305.     if([delegate respondsTo:@selector(openFile:ok:)]) handler = delegate;
  306.     else handler = self;
  307.     
  308.     // Open files one by one.
  309.     if([iconPath count] <= 0) return nil;
  310.     fileName = (char *)[iconPath elementAt:0];
  311.     nxt = strchr(fileName, '\t');
  312.     success = self;
  313.     while(fileName)
  314.     {
  315.             if(nxt) *nxt = 0;
  316.         [handler openFile:fileName ok:&ok];
  317.         if(!ok) success = nil;
  318.         if(nxt)
  319.         {
  320.         *nxt = '\t';
  321.             fileName = nxt+1;
  322.         nxt = strchr(fileName, '\t');
  323.         }
  324.         else fileName = nxt = 0;
  325.     }
  326.     return success;
  327.     }
  328.     else
  329.     {
  330.     // Ignore higher multi-clicks.
  331.     return self;
  332.     }
  333. }
  334.  
  335. - mouseDragged:(NXEvent *)theEvent
  336. {
  337.     NXRect iconRect;
  338.     int ok;
  339.     char *fileName, *nxt;
  340.     id success, handler;
  341.  
  342.     // Check if valid drag.
  343.     if(!(isDragging && isDraggable && [cell icon] && [iconPath count]>0))
  344.     { isDragging = NO; return self; }
  345.     
  346.     // Prep file(s) for drag; delegate this task if possible.
  347.     if([delegate respondsTo:@selector(prepFile:ok:)]) handler = delegate;
  348.     else handler = self;
  349.     if([iconPath count] <= 0) return nil;
  350.     fileName = (char *)[iconPath elementAt:0];
  351.     nxt = strchr(fileName, '\t');
  352.     success = self;
  353.     while(fileName)
  354.     {
  355.     if(nxt) *nxt = 0;
  356.     [handler prepFile:fileName ok:&ok];
  357.     if(!ok) success = nil;
  358.     if(nxt)
  359.     {
  360.         *nxt = '\t';
  361.         fileName = nxt+1;
  362.         nxt = strchr(fileName, '\t');
  363.     }
  364.     else fileName = nxt = 0;
  365.     }
  366.     if(!success) { isDragging = NO; return success; }
  367.  
  368.     // Try to drag icon out.
  369.     [self getBounds:&iconRect];
  370.     [cell getIconRect:&iconRect];
  371.     NX_WIDTH(&iconRect) = NX_HEIGHT(&iconRect) = 48.0;
  372.     success = [super dragFile:(const char *)[iconPath elementAt:0] 
  373.     fromRect:&iconRect slideBack:YES event:&dragFromEvent];
  374.     isDragging = NO;
  375.  
  376.     // Take care of no-hold-on-drag.
  377.     if(!isHoldOnDrag)
  378.     {
  379.     // Successful drag: clear path and icon to initial state.
  380.     if(success) [self clear:self];
  381.     // Unsuccessful drag: redisplay old icon in case it left.
  382.     else [cell setIcon:iconName];
  383.     }
  384.     return success;
  385. }
  386.  
  387. - mouseUp:(NXEvent *)theEvent
  388. {
  389.     isDragging = NO;
  390.     return self;
  391. }
  392.  
  393. - (BOOL)isScreenPointInView:(double)x :(double)y
  394. {
  395.     NXPoint point;
  396.     NXRect rect;
  397.  
  398.     // If (x,y) is not in this IconWell, try next one in chain.
  399.     point.x = x; point.y = y;
  400.     [window convertScreenToBase:&point];
  401.     [self convertPoint:&point fromView:nil];
  402.     [self getBounds:&rect];
  403.     if(NXMouseInRect(&point, &rect, [self isFlipped])) return YES;
  404.     else return NO;
  405. }
  406.  
  407. - (int)iconEntered:(int)windowNum at:(double)x :(double)y
  408.     iconWindow:(int)iconWindowNum iconX:(double)iconX iconY:(double)iconY
  409.     iconWidth:(double)iconWidth iconHeight:(double)iconHeight
  410.     pathList:(char *)pathList
  411. {
  412.     // Temporarily display new image if (x,y) is in this IconWell.
  413.     if(!isDroppable || ![self isScreenPointInView:x :y])
  414.         { isDropping = NO; return 0; }
  415.     [cell setIcon:"Blank.tiff"];
  416.     [cell setIcon:newIconName];
  417.     isDropping = YES; return 0;
  418. }
  419.  
  420. - (int)iconMovedTo:(double)x :(double)y
  421. {
  422.     // Check if (x,y) moved in or out of this IconWell.
  423.     if(!isDroppable) return 0;
  424.     else if(![self isScreenPointInView:x :y])
  425.     {
  426.         if(!isDropping) return 0;
  427.  
  428.     // Mouse left; restore old image (unless no-hold-on-drag thing).
  429.     [cell setIcon:"Blank.tiff"];
  430.     if(!isDragging || isHoldOnDrag) [cell setIcon:iconName];
  431.     isDropping = NO; return 0;
  432.     }
  433.     else if(!isDropping)
  434.     {
  435.         // Mouse entered; display new image.
  436.     [cell setIcon:"Blank.tiff"];
  437.     [cell setIcon:newIconName];
  438.     isDropping = YES; return 0;
  439.     }
  440.     return 0;
  441. }
  442.  
  443. - (int)iconExitedAt:(double)x :(double)y
  444. {
  445.     if(!isDroppable || !isDropping) return 0;
  446.  
  447.     // Mouse left; restore old image (unless no-hold-on-drag thing).
  448.     [cell setIcon:"Blank.tiff"];
  449.     if(!isDragging || isHoldOnDrag) [cell setIcon:iconName];
  450.     isDropping = NO; return 0;
  451. }
  452.  
  453. - (int)iconReleasedAt:(double)x :(double)y ok:(int *)flag
  454. {
  455.     const char *path;
  456.     
  457.     // Check if (x,y) moved in or out of this IconWell.
  458.     *flag = 0;
  459.     if(!isDroppable) return 0;
  460.     else if(![self isScreenPointInView:x :y]
  461.         || !(path = [[IconWellControl controlFor:window] newIconPath]))
  462.     {
  463.         if(!isDropping) return 0;
  464.  
  465.     // Mouse left; restore old image (unless no-hold-on-drag thing).
  466.     [cell setIcon:"Blank.tiff"];
  467.     if(!isDragging || isHoldOnDrag) [cell setIcon:iconName];
  468.     isDropping = NO; return 0;
  469.     }
  470.     else if(!isDropping)
  471.     {
  472.         // Mouse entered; display new image.
  473.     [cell setIcon:"Blank.tiff"];
  474.     [cell setIcon:newIconName];
  475.     }
  476.  
  477.     // Replace path and icon with new ones; notify target.
  478.     [iconPath setNumSlots:(strlen(path)+1)];
  479.     strcpy((char *)[iconPath elementAt:0], path);
  480.     [cell setIcon:"Blank.tiff"];
  481.     [[NXImage findImageNamed:iconName] free];
  482.     [[NXImage findImageNamed:newIconName] setName:iconName];
  483.     [cell setIcon:iconName];
  484.     [self sendAction:[cell action] to:[cell target]];
  485.  
  486.     // Accept icon and end drop.
  487.     isDropping = NO;
  488.     *flag = 1;
  489.     return 0;
  490. }
  491.  
  492. @end
  493.